﻿#region Imported Libraries
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
#endregion
namespace org.webguvenligi.NSecureImageLib.GraphicFileFormats.Bitmap{
//http://www.vsj.co.uk/articles/display.asp?id=501
//LONG==INT32
//WORD==UINT16
//DWORD==UINT32
//short = short
//    typedef struct tagBITMAPINFOHEADER {    /* bmih */
//    DWORD   biSize;
//    LONG    biWidth;
//    LONG    biHeight;
//    WORD    biPlanes;
//    WORD    biBitCount;
//    DWORD   biCompression;
//    DWORD   biSizeImage;
//    LONG    biXPelsPerMeter;
//    LONG    biYPelsPerMeter;
//    DWORD   biClrUsed;
//    DWORD   biClrImportant;
//} BITMAPINFOHEADER;
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BITMAPINFOHEADER 
    {        
        public UInt32 Size; //4
        public Int32 Width;
        public Int32 Height;
        public ushort Planes;
        public ushort BitCount;
        public uint Compression;
        public uint SizeImage;
        public int XPelsPerMeter;
        public int YPelsPerMeter;
        public uint ClrUsed;
        public uint ClrImportant;
    }

//    typedef struct tagBITMAPCOREHEADER {    /* bmch */
//    DWORD   bcSize;
//    short   bcWidth;
//    short   bcHeight;
//    WORD    bcPlanes;
//    WORD    bcBitCount;
//} BITMAPCOREHEADER;
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BITMAPCOREHEADER{
        public UInt32 Size;
        public short Width;
        public short Height;
        public UInt16 Planes;
        public UInt16 BitCount;
    }

//typedef struct tagRGBQUAD {     /* rgbq */
//    BYTE    rgbBlue;
//    BYTE    rgbGreen;
//    BYTE    rgbRed;
//    BYTE    rgbReserved;
//} RGBQUAD;
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct RGBQUAD
    {
        public byte rgbBlue;
        public byte rgbGreen;
        public byte rgbRed;
        public byte rgbReserved;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BITMAPFILEHEADER{
        public UInt16 Type;  //2
        public UInt32 Size;  // 4
        public UInt16 Reserved1; //2
        public UInt16 Reserved2; // 2
        public UInt32 OffBits; // 4
    }
    public sealed class BitmapParser : BaseImageParser {
        public BitmapParser(Stream bitmapStream)
            : base(bitmapStream){
        }
        public BITMAPINFOHEADER InfoHeader
        {
            get;
            private set;
        }
        public BITMAPFILEHEADER FileHeader
        {
            get;
            private set;
        }
        public BITMAPCOREHEADER CoreHeader
        {
            get;
            private set;
        }
        protected override bool ParseImage()
        {
            unsafe
            {                
                if (ImageStream.Length < (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)))
                    return false;

                ImageStream.Seek(0, SeekOrigin.Begin);
                byte[] imageBytes = new byte[ImageStream.Length];
                ImageStream.Read(imageBytes, 0, (int)ImageStream.Length);

                fixed (byte* p_Data = imageBytes)
                {
                    BITMAPFILEHEADER* bitmapHeader =
                        (BITMAPFILEHEADER*)p_Data;
                    
                    FileHeader = *bitmapHeader;

                    //File Size Should Match With The Header
                    if (bitmapHeader->Size != ImageStream.Length)
                        return false;
                    
                    BITMAPINFOHEADER* bitmapInfoHeader =
                        (BITMAPINFOHEADER*)(sizeof(BITMAPFILEHEADER) + p_Data);

                    //Ok Then BITMAPCOREINFOHEADER Can Be Used Lets Check For That
                    if (bitmapInfoHeader->Size != sizeof(BITMAPINFOHEADER)){
                        //Clear BitmapInfoHeader Structure
                        bitmapInfoHeader = null;
                        BITMAPCOREHEADER* bitmapCoreHeader =
                            (BITMAPCOREHEADER*)(sizeof(BITMAPFILEHEADER) + p_Data);

                        this.CoreHeader = *bitmapCoreHeader;
                    }
                    else{
                        
                        if (bitmapInfoHeader->Planes != 1)
                            return false;
                        else if (bitmapInfoHeader->BitCount != 1 &&
                                bitmapInfoHeader->BitCount != 4 &&
                                bitmapInfoHeader->BitCount != 8 &&
                                bitmapInfoHeader->BitCount != 24)
                            return false;

                        InfoHeader = *bitmapInfoHeader;
                    }
                }
            }
            return true;
        }

        protected override string FormatExceptionMessage
        {
            get { return "Not a valid bitmap file"; }
        }

        protected override byte[] HeaderSignature
        {
            get { return new byte[]{0x42, 0x4D}; }
        }

        public override void WriteImage(Stream targetStream, bool removeMetadataTags)
        {
            throw new NotImplementedException();
        }
    }
}